#ifndef PARAFLUIDSIMULATION_H
#define PARAFLUIDSIMULATION_H

#include "parafluidatom.h"
#include "parafluidcolor.h"
#include "parafluidemitter.h"
#include "parafluidmesh.h"
#include "parafluidsph.h"
#include "parafluidsubstance.h"
#include "parafluidvector.h"

#include <QMutex>
#include <QSemaphore>
#include <QThread>
#include <QVector>

namespace ParaFluid
{
	
	/**
	* Simulation
	*/
	class Simulation
		: public QThread
	{
		
		Q_OBJECT
		
	signals:
		
		/**
		* Stepped
		* @note After receiving this event, SPH will be locked for you. Invoke nextStep() to release it
		* @warning Only one receiver should ever be connected to this Signal
		* @warning Invoking nextStep() twice yield undefined behavior
		*/
		void stepped();
		
		/**
		* Reseted
		*/
		void reseted();
		
	public:
		
		/**
		* Destructor
		*/
		~Simulation();
		
		/**
		* Getter for Instance
		*/
		static Simulation & instance();
		
		/**
		* Getter for sensible default Substance
		*/
		static const Substance & defaultSubstance();
		
		/**
		* Getter for sensible default number of Atoms
		*/
		static int defaultAtoms();
		
		/**
		* Getter for sensible default number of Buckets
		*/
		static int defaultBuckets();
		
		/**
		* Acessor for SPH
		*/
		SPH & sph();
		
		/**
		* Reset the Simulation with the Specified number of Buckets and Atoms, rate of emittion and position of the emitter
		*/
		void resetSPH(int buckets, int atoms, const Substance & substance, int rate, const Vector & emitterPosition);
		
		/**
		* Pause/Resume Simulation
		*/
		void pause();
		
		/**
		* Continue Simulation
		*/
		void nextStep();
		
	protected:
		
		/**
		* Thread Main
		*/
		void run();
		
	private:
		
		/**
		* Instance
		*/
		static Simulation * _instance;
		
		/**
		* Continue
		*/
		bool _continue;
		
		/**
		* Semaphore Step
		*/
		QSemaphore _semaphoreStep;
		
		/**
		* SPH
		*/
		SPH * _sph;
		
		/**
		* Mutex for SPH
		*/
		QMutex _mutexSPH;
		
		/**
		* Mesh
		*/
		Mesh _mesh;
		
		/**
		* Atom Emmiter
		*/
		Emitter _emitter;
		
		/**
		* Default Constructor
		*/
		Simulation();
		
		/**
		* Copy Constructor
		*/
		Simulation(const Simulation & other);
		
		/**
		* Assignment Operator
		*/
		Simulation & operator= (const Simulation & rhs);
		
	};
	
}

#endif

